package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanhua.common.enums.TopState;
import com.tanhua.common.mapper.BlackListMapper;
import com.tanhua.common.pojo.BlackList;
import com.tanhua.dubbo.server.enums.CommentPublishIdType;
import com.tanhua.dubbo.server.enums.CommentType;
import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.common.enums.PassType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created with IntelliJ IDEA.
 *
 * @ClassName: QuanZiApiImpl
 * @Author:王晨晗
 * @Description: 圈子接口实现类
 * @Date: 2021/12/30/21:58
 */
@Service
@Slf4j
public class QuanZiApiImpl implements QuanZiApi{
    //评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "QUANZI_COMMENT_";
    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIEK_REDIS_KEY_PREFIX = "USER_LIKE_";
    //用户是否喜欢的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX = "USER_LOVE_";

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String , String> redisTemplate;

    @Autowired
    private TimeLineService timeLineService;

    @Autowired
    private IdService idService;

    /**
     * @description 查询用户的好友动态信息
     * @param userId    当前用户id
     * @param page      当前页
     * @param pagesize 每页显示条数
     * @updateTime 2021/12/30 21:59
     */
    @Override
    public List<Publish> queryPublishList(long userId, Integer page, Integer pagesize , List<Object> collectId) {
        //查询时间线表, 获取到好友发布动态的id
        List<Object> tIds = queryTimeLine(userId, page, pagesize);
        //判断是否为空
        if (tIds==null){
            return new ArrayList<Publish>(); //为空 返回空集合
        }

        //查询动态表,条件(动态id在查询出的好友动态id中,按时间降序排列)
        Query query = Query.query(Criteria.where("id").in(tIds)
                .and("messageType").is(PassType.PERSONPASS.getValue())
                .and("userId").nin(collectId));
        PageRequest pageRequest = PageRequest.of(page-1,pagesize,Sort.by(Sort.Order.desc("created")));
        //添加分页条件
        query.with(pageRequest);
        //执行查询
        List<Publish> publishList = mongoTemplate.find(query, Publish.class);
        //返回查询结果
        return publishList;
    }

    /**
     * @description 根据用户id获取其时间线表中的动态发布id
     * @param userId    用户id
     * @param page      当前页
     * @param pagesize 每页条数
     * @updateTime 2021/12/30 22:05
     */
    @Override
    public List<Object> queryTimeLine(long userId, Integer page, Integer pagesize) {
        //分页条件(按时间降序)
       // PageRequest pageRequest = PageRequest.of(page- 1 , pagesize , Sort.by(Sort.Order.desc("date")));
        // 查询条件
       // Query query = new Query().with(pageRequest);
        //构建查询条件
        Query query = new Query();
        // 查询mongo数据库
        List<TimeLine> timeLineList = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);
        //判断时间线表的数据是否为空,
        if (CollUtil.isEmpty(timeLineList)){
            return null;        //为空,返回null
        }
        //获取时间线表中的动态发布id的集合
        List<Object> ids = CollUtil.getFieldValues(timeLineList, "publishId");
        //返回集合数据
        return ids;
    }


    /**
     * @description 根据用户id 获取推荐动态信息
     * @param userId    当前用户id
     * @param page  当前页
     * @param pagesize   每页条数
     * @updateTime 2021/12/30 23:28
     */
    @Override
    public List<Publish> queryRecommendPublishList(long userId, Integer page, Integer pagesize) {
        //redis中hash的键值(保存推荐动态的id)
        String redisKey = "QUANZI_PUBLISH_RECOMMEND_" + userId;
        //获取redis中用户的推荐动态的id
        String redisValue = redisTemplate.opsForValue().get(redisKey);
        //判断是否有推荐动态
        if (StrUtil.isEmpty(redisValue)){
            return new ArrayList<Publish>(); //为空 返回空集合
        }
        //对获取出的推荐动态id ,进行分割处理
        List<String> split = StrUtil.split(redisValue, ",");
        //将String类型的id转换成long
        List<Long> pids = split.stream().map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
        //构建分页条件,按时间降序排列
        PageRequest pageRequest = PageRequest.of(page- 1 , pagesize, Sort.by(Sort.Order.desc("created")));
        //构建查询条件:动态的id 在redis推荐id中 , 分页

        Query query = Query.query(Criteria.where("pid").in(pids).and("messageType").is(PassType.PERSONPASS.getValue())).with(pageRequest);
        //执行查询
        List<Publish> publishList = mongoTemplate.find(query, Publish.class);


        //返回查询结果
        return publishList;
    }

    /**
     * @description 保存发布的动态
     * @param publish   动态信息
     * @updateTime 2021/12/31 10:29
     * @return: java.lang.String
     */
    @Override
    public String savePublish(Publish publish) {
        //设置动态的消息状态信息
        publish.setMessageType(PassType.WAIT.getValue());
        //设置动态置顶状态
        publish.setTopState(TopState.up.getValue());
        //设置发布动态的id
        publish.setId(ObjectId.get());
        try {
            //设置自增长的pid
            Long pid = idService.createId(IdType.PUBLISH);      //获取自增长pid
            publish.setPid(pid);
            //动态发布时间
            publish.setCreated(System.currentTimeMillis());
            //保存到publish表中
            mongoTemplate.save(publish);

            //保存到相册表
            Album album = new Album();
            //设置相册表id
            album.setId(ObjectId.get());
            //设置动态id
            album.setPublishId(publish.getId());
            //设置创建时间
            album.setCreated(System.currentTimeMillis());
            //保存到相册表
            mongoTemplate.save(album , "quanzi_album_" + publish.getUserId());

            //写入好友时间线表(异步写入)
            timeLineService.saveTimeLine(publish.getUserId() , publish.getId());
        } catch (Exception e) {
            log.error("发布动态失败~ publish =" + publish.getId() , e);
            return null;
        }

        //返回保存动态的id;
        return publish.getId().toHexString();
    }

    /**
     * @description 根据动态id , 查询动态信息
     * @param id  动态id
     * @return
     */
    @Override
    public Publish queryPublishById(String id) {
        return mongoTemplate.findById(new ObjectId(id), Publish.class);
    }

    /**
     * @description  (动态/评论/视频)取消点赞或喜欢功能
     * @param userId    当前用户id
     * @param publishId     (动态/评论/视频) id
     * @param commentType   取消点赞还是取消喜欢
     * @return
     */
    @Override
    public Boolean disLikeOrLove(Long userId , String publishId , CommentType commentType){
        //判断用户是否喜欢/点赞 ,否 返回false;
        if (!queryUserIf(userId , publishId , commentType)){
            return false;
        }
        //点赞/喜欢,则将此条评论在评论表删除
        Boolean result = removeComment(userId, publishId, commentType);
        //判断是否删除成功
        if (!result){
            return false;  //删除失败,返回false;
        }

        //删除成功 , 将redis中点赞/喜欢数减一
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = commentType.toString();        // 点赞/喜欢数
        redisTemplate.opsForHash().increment(redisKey , hashKey , -1);

        //将该用户的点赞/喜欢 信息在redis中删除;
        //判断是喜欢还是点赞的评论类型
        switch (commentType.getType()){
            case 1 :        //点赞
                hashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
                break;
            case 3 :        //喜欢
                hashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
                break;
        }
        //从redis中删除
        redisTemplate.opsForHash().delete(redisKey , hashKey);
        //返回取消点赞/喜欢的结果
        return true;
    }


    /**
     * @description 查询点赞/喜欢数
     * @param publishId 动态/视频/评论 id
     * @param commentType 评论类型
     * @return
     */
    @Override
    public Long queryCount(String publishId, CommentType commentType) {
        //从redis中查询点赞或喜欢数
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = commentType.toString();
        Object value = redisTemplate.opsForHash().get(redisKey, hashKey);
        //判断是否命中, 命中则返回, 否则查询数据库
        if (ObjectUtil.isNotEmpty(value)){
            return Convert.toLong(value);
        }
        //查询数据库, 获取点赞/喜欢数
        Long count = queryCommentCount(publishId , commentType);

        //存入Redis中
        redisTemplate.opsForHash().put(redisKey,hashKey,String.valueOf(count));
        //返回点赞/喜欢数
        return count;
    }


    /**
     * @description 判断用户对该(动态/评论/视频)是否喜欢/点赞
     * @param userId        当前用户id
     * @param publishId  (动态/评论/视频)的id
     * @param commentType   评论类型:点赞/喜欢
     * @return
     */
    @Override
    public Boolean queryUserIf(Long userId, String publishId, CommentType commentType) {
        //创建redis中对应键值
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        //判断评论的类型,得到对应的key值 ,即redis中的字段
        String hashKey = null;
        switch (commentType.getType()){
            case 1 :        //点赞
                hashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
                break;
            case 3 :        //喜欢
                hashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
                break;
        }

        //获取redis中对应key的值(当前用户是否点赞/喜欢)
        Object data = redisTemplate.opsForHash().get(redisKey, hashKey);
        //判断是否为空
        if (ObjectUtil.isNotEmpty(data)){
            //不为空,判断 是否为1 , 只有是1才代表点赞/喜欢了
            return StrUtil.equals(Convert.toStr(data) , "1");
        }

        //redis中获取不到用户是否点赞/喜欢的信息时, 查询数据库,进行判断
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))   //(动态/评论/视频)的id
                .and("userId").is(userId)       //用户id
                .and("commentType").is(commentType.getType()));     //评论类型
        long count = mongoTemplate.count(query, Comment.class);     //获取查询的条数
        //判断是否查询到数据
        if (count == 0){
            return false;       //未查询到表示没喜欢/点赞,返回false即可
        }
        //查询到数据 表示喜欢/点赞了.则存入redis中
        redisTemplate.opsForHash().put(redisKey , hashKey , "1");
        //返回true;
        return true;
    }

    /**
     * @description 查询动态/视频的评论列表
     * @param publishId    动态/视频 id
     * @param page          当前页
     * @param pagesize      每页条数
     * @return
     */
    @Override
    public List<Comment> queryCommentList(String publishId, Integer page, Integer pagesize) {
        //构建分页条件,按创建时间升序
        PageRequest pageRequest = PageRequest.of(page - 1 , pagesize , Sort.by(Sort.Order.asc("created")));
        //构建查询条件,指定查询的id , 查询类型:评论
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.COMMENT.getType())).with(pageRequest);
        //执行查询 获取结果集合
        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
        //返回结果集合
        return commentList;
    }

    /**
     * @description 发表评论
     * @param userId        评论者id
     * @param publishId     被评论的动态/视频的id
     * @param content       评论内容
     * @param commentPublishIdType    所评论的类型(即是视频还是动态的评论)
     * @return
     */
    @Override
    public Boolean saveComment(Long userId, String publishId, String content , CommentPublishIdType commentPublishIdType) {
        //调用保存评论方法,获取保存评论结果
        Boolean result = saveComment(userId, publishId, CommentType.COMMENT, content, commentPublishIdType);
        //如果评论发布成功 ,redis加一
        if (result){
            String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
            String hashkey = "COMMENT";
            redisTemplate.opsForHash().increment(redisKey , hashkey , 1);
        }
        //返回保存结果
        return result;
    }

    /**
     * @description 查询对我的喜欢/点赞/评论列表
     * @param userId  当前用户id
     * @param page 当前页
     * @param pageSize 每页条数
     * @param commentType 查询列表的类型
     * @return
     */
    @Override
    public List<Comment> queryCommentListByUser(Long userId, Integer page, Integer pageSize, CommentType commentType) {
        //设置分页条件
        PageRequest pageRequest = PageRequest.of(page-1 , pageSize , Sort.by(Sort.Order.desc("created")));
        //设置查询条件
        Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(commentType.getType())).with(pageRequest);
        //查询数据库
        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
        //返回评论集合
        return commentList;
    }

    /**
     * @description 查询相册表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public List<Publish> queryAlbumList(Long userId, Integer page, Integer pageSize) {
        //构建分页条件
        PageRequest pageRequest = PageRequest.of(page -1 , pageSize , Sort.by(Sort.Order.desc("created")));
        //构建查询条件
        Query query = new Query().with(pageRequest);
        //查询相册表
        List<Album> albums = mongoTemplate.find(query, Album.class, "quanzi_album_" + userId);
        //判断是否为空
        if (CollUtil.isEmpty(albums)){
            return null;
        }
        //不为空,获取相册表中的动态id
        List<Object> publishIdList = CollUtil.getFieldValues(albums, "publishId");
        //查询动态表
        Query query1 = Query.query(Criteria.where("id").in(publishIdList)).with(Sort.by(Sort.Order.desc("created")));
        List<Publish> publishList = mongoTemplate.find(query1, Publish.class);

        return publishList;
    }

    /*
    *   根据pid查询数据
    * */
    @Override
    public Publish queryPublishByPId(String id) {
        Query query = Query.query(Criteria.where("pid").is(Convert.toLong(id)));
        Publish publish = mongoTemplate.findOne(query, Publish.class);
        return publish;
    }



    /**
     * @description 点赞/喜欢 指定是动态/动态评论点赞/视频评论点赞
     * @param userId        当前用户id
     * @param publishId     动态/视频/评论的id
     * @param commentType      评论类型:点赞/喜欢
     * @param commentPublishIdType  publishId的类型: 动态/评论/小视频
     * @return
     */
    @Override
    public Boolean likeOrLoveComment(Long userId, String publishId, CommentType commentType, CommentPublishIdType commentPublishIdType) {
        //判断用户是否喜欢/点赞 ,是则返回false;
        if (queryUserIf(userId , publishId, commentType)){
            return false;
        }
        //没有点赞/喜欢,则将此条评论写入评论表中(告知保存评论的方法,是对视频还是动态的点赞/喜欢操作,让其查询相应的表)
        Boolean result = saveComment(userId, publishId, commentType, null , commentPublishIdType);
        //判断是否保存成功
        if (!result){
            return false;  //保存失败,返回false;
        }

        //保存成功 , 将redis中点赞/喜欢数加一
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashkey = commentType.toString();
        redisTemplate.opsForHash().increment(redisKey , hashkey , 1);

        //将该用户的点赞/喜欢 信息写入到redis中;
        //判断是喜欢还是点赞
        switch (commentType.getType()){
            case 1 :    //点赞
                hashkey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
                break;
            case 3 :    //喜欢
                hashkey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
                break;
        }
        //将结果写入redis中
        redisTemplate.opsForHash().put(redisKey , hashkey , "1");
        //返回响应true
        return true;
    }

    /**
     * @description    保存评论信息
     * @param userId   当前用户id(发布评论者)
     * @param publishId     被评论的id(动态或视频)
     * @param commentType   评论的类型(点赞/喜欢/评论)
     * @param context       评论内容
     * @param commentPublishIdType  明确是视频还是动态还是评论的评论操作
     * @return
     */
    private Boolean saveComment(Long userId, String publishId, CommentType commentType, String context , CommentPublishIdType commentPublishIdType) {
        try {
            //创建comment对象,保存评论信息
            Comment comment = new Comment();
            comment.setId(ObjectId.get());      //主键id
            comment.setUserId(userId);          //当前用户id,即评论者id
            comment.setPublishId(new ObjectId(publishId));  //发布动态的id,即对哪条动态点的赞
            comment.setCommentType(commentType.getType());  //评论类型
            comment.setContent(context);        //评论内容
            comment.setCreated(System.currentTimeMillis());     //评论时间

            //因为传递的publishid,不仅仅指代动态的id , 还有可能是评论的id ,小视频的id
            //因此当动态表查不到数据时, 在查询一下评论表,看是否是评论表中的id(即是对评论的点赞)
            switch (commentPublishIdType.getType()){
                case 1:     //动态
                    //获取动态发布者的id信息(只需要其id属性)
                    Publish publish = queryPublishById(publishId);
                    //判断是否获取到动态的信息
                    if (ObjectUtil.isNotEmpty(publish)){
                        comment.setPublishUserId(publish.getUserId());          //保存被评论者的id
                    }
                    break;
                case 2:     //评论
                    //查询评论表,获取评论信息
                    Comment myComment = queryCommentById(publishId);
                    //判断是否获取到评论信息
                    if (ObjectUtil.isNotEmpty(myComment)){
                        comment.setPublishUserId(myComment.getUserId());        //保存被评论者的id
                    }
                    break;
                case 3:     //视频
                    //查询视频表, 获取视频信息
                    Video video = queryVideoById(publishId);
                    if (ObjectUtil.isNotEmpty(video)){
                        comment.setPublishUserId(video.getUserId());            //保存被评论者的id
                    }
                    break;
            }

            //保存评论信息
            mongoTemplate.save(comment);
            //返回保存结果
            return true;
        } catch (Exception e) {
            //保存失败,打印日志
            log.error("保存Comment出错~ userId = " + userId + ", publishId = " + publishId + ", commentType = " + commentType, e);
        }
        //返回保存失败
        return false;
    }

    /**
     * @description 根据id查询视频表
     * @param publishId  视频id
     * @return
     */
    private Video queryVideoById(String publishId) {
        return mongoTemplate.findById(new ObjectId(publishId) , Video.class);
    }

    /**
     * @description 根据id查询评论表
     * @param publishId  评论id
     * @return
     */
    private Comment queryCommentById(String publishId) {
        return mongoTemplate.findById(new ObjectId(publishId) , Comment.class);
    }

    /**
     * @description 删除评论表中的评论信息
     * @param userId 当前用户id
     * @param publishId  (动态/评论)的id
     * @param commentType   评论的类型(点赞/喜欢)
     * @return
     */
    private Boolean removeComment(Long userId, String publishId, CommentType commentType) {
        //构建查询条件
        Query query = Query.query(Criteria.where("userId").is(userId)   //用户id
                .and("publishId").is(new ObjectId(publishId))       //(动态/评论)的id
                .and("commentType").is(commentType.getType()));     //评论类型
        //删除数据并返回删除结果
        return mongoTemplate.remove(query , Comment.class).getDeletedCount() > 0;
    }

    /**
     * @description 查询数据库中对应评论类型的数量
     * @param publishId  动态/视频/评论 id
     * @param commentType 评论类型
     * @return
     */
    private Long queryCommentCount(String publishId, CommentType commentType) {
        //构建查询条件
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType()));
        //查询评论表,返回数据
        return mongoTemplate.count(query , Comment.class);
    }

    /**
     *  修改点赞数
     * @param likeCount
     */
    @Override
    public void updatePublishLikeCount(Integer likeCount,ObjectId publishId) {
        Query query = Query.query(Criteria.where("id").is(publishId));
        Update update = new Update();
        update.set("likeCount",likeCount);
        mongoTemplate.upsert(query, update, Publish.class);
    }

    /**
     * 修改评论数
     * @param commentCount
     */
    @Override
    public void updateCommentCount(Integer commentCount,ObjectId publishId) {
        Query query = Query.query(Criteria.where("id").is(publishId));
        Update update = new Update();
        update.set("commentCount",commentCount);
        mongoTemplate.upsert(query, update, Publish.class);
    }
}
